package com.ruoyi.tester.executor;

import com.mashape.unirest.http.HttpResponse;
import com.ruoyi.tester.domain.InterfaceCallLog;
import com.ruoyi.tester.domain.TaskCallLog;
import com.ruoyi.tester.domain.TaskInterfaceEffectiveInfo;
import com.ruoyi.tester.executor.compile.KeyWords;
import com.ruoyi.tester.executor.compile.Literal;
import com.ruoyi.tester.executor.compile.WordsGenerator;
import com.ruoyi.tester.executor.expression.ExpressionAnalysis;
import com.ruoyi.tester.executor.expression.ExpressionContainer;
import com.ruoyi.tester.executor.http.XzfHttpResult;
import com.ruoyi.tester.mapper.TesterExecutorMapper;
import org.json.JSONArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * @author SongJun
 * @description TODO:
 * @date 2022/4/15 16:31
 */
@Service
public class ExtraHandlerImpl implements IExtraHandler {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    TesterExecutorMapper testerExecutorMapper;

    @Override
    public void cureDependParam(TaskInterfaceEffectiveInfo interfaceInfo, Map<Long, String> calledStatus){
        if(StringUtils.hasLength(interfaceInfo.getDependParam())){
            String dependParam = interfaceInfo.getDependParam();
            List<Long> relationIdList = interfaceInfo.getDependRelationIdList();
            JSONArray innate = new JSONArray(relationIdList.size());
            for(Long relationId : relationIdList){
                //将依赖接口的返回结果合并到JSONArray中
                String response = calledStatus.get(relationId);
                innate.put(response);
            }
            AnalysisResult depends = analysisCodeBlock(dependParam, interfaceInfo, innate);
            for(Map.Entry<String, Object> entry : depends.getVm().entrySet()){
                String key = "${" + entry.getKey() + "}";
                replaceDependValue(interfaceInfo, key, entry.getValue());
            }
            for (int i=0; i < depends.getVa().length; i++){
                String key = "$[" + i + "]";
                replaceDependValue(interfaceInfo, key, depends.getVa()[i]);
            }
        }
    }

    private void replaceDependValue(TaskInterfaceEffectiveInfo interfaceInfo, String key, Object value){
        if (interfaceInfo.getMapping() != null && interfaceInfo.getMapping().contains(key)){
            interfaceInfo.setMapping(interfaceInfo.getMapping().replace(key, value.toString()));
        }
        if (interfaceInfo.getQueryParam() != null && interfaceInfo.getQueryParam().contains(key)){
            interfaceInfo.setQueryParam(interfaceInfo.getQueryParam().replace(key, value.toString()));
        }
        if (interfaceInfo.getBodyParam() != null && interfaceInfo.getBodyParam().contains(key)){
            interfaceInfo.setBodyParam(interfaceInfo.getBodyParam().replace(key, value.toString()));
        }
        if (interfaceInfo.getPathParam() != null && interfaceInfo.getPathParam().contains(key)){
            interfaceInfo.setPathParam(interfaceInfo.getPathParam().replace(key, value.toString()));
        }
    }

    @Override
    public boolean judgeResponse(TaskInterfaceEffectiveInfo interfaceInfo, XzfHttpResult httpResponse){
        if(httpResponse == null){
            return false;
        }
        boolean ret = true;
        try {
            if (StringUtils.hasLength(interfaceInfo.getJudgeExpression())) {
                String response = httpResponse.getBody();
                JSONArray innate = new JSONArray();
                innate.put(response);
                AnalysisResult analysisRet = analysisCodeBlock(interfaceInfo.getJudgeExpression(), interfaceInfo, innate);
                if(analysisRet == null || analysisRet.getVa().length <= 0){
                    return false;
                }
                for (Object obj : analysisRet.getVa()){
                    Boolean bo = (Boolean)obj;
                    ret = ret && bo;
                }
            } else {
                ret = httpResponse.getStatus() == 200;
            }
        } catch (Exception e){
            logger.error("",e);
            ret = false;
        }
        return ret;
    }

    /**
     * @description
     * 在真正调用接口并完成之前, 先在calledStatus中设置valud为empty的key值,
     * 其他线程可通过是否存在key值判断是否需要执行此接口, 避免依赖了多个父接口的接口被多次执行
     * @author SongJun
     * @date 2022/4/21 15:58
     */
    @Override
    public synchronized List<TaskInterfaceEffectiveInfo> followedInterfaceInfoList(Long relationId, List<TaskInterfaceEffectiveInfo> effectiveList, Map<Long, String> calledStatus) {
        List rst = new LinkedList();
        for (TaskInterfaceEffectiveInfo item : effectiveList){
            if(item.getDependRelationIdList().contains(relationId)){
                if(calledStatus.containsKey(item.getRelationId())){
                    continue;
                }
                boolean ready = true;
                for (Long dependRelationId : item.getDependRelationIdList()){
                    if(!calledStatus.containsKey(dependRelationId)
                            || IExtraHandler._PROCESSING_.equals(calledStatus.get(dependRelationId))){
                        ready = false;
                        break;
                    }
                }
                if(ready){
                    rst.add(item);
                    calledStatus.put(item.getRelationId(), IExtraHandler._PROCESSING_);
                }
            }
        }
        return rst;
    }

    @Override
    @Transactional
    public void saveResult(InterfaceCallLog interfaceCallLog, String progres) {
        try {
            if(interfaceCallLog.getFinalUrl() != null && interfaceCallLog.getFinalUrl().length() > 512){
                interfaceCallLog.setFinalUrl(interfaceCallLog.getFinalUrl().substring(0, 512));
            }
            if(interfaceCallLog.getBodyParam() != null && interfaceCallLog.getBodyParam().length() > 21845){
                interfaceCallLog.setBodyParam(interfaceCallLog.getBodyParam().substring(0, 21845));
            }
            if(interfaceCallLog.getHeaders() != null && interfaceCallLog.getHeaders().length() > 512){
                interfaceCallLog.setHeaders(interfaceCallLog.getHeaders().substring(0, 512));
            }
            if(interfaceCallLog.getResponse() != null && interfaceCallLog.getResponse().length() > 21845){
                interfaceCallLog.setResponse(interfaceCallLog.getResponse().substring(0, 21845));
            }
            if(interfaceCallLog.getResponseHeaders() != null && interfaceCallLog.getResponseHeaders().length() > 512){
                interfaceCallLog.setResponseHeaders(interfaceCallLog.getResponseHeaders().substring(0, 512));
            }
            if(interfaceCallLog.getException() != null && interfaceCallLog.getException().length() > 512){
                interfaceCallLog.setException(interfaceCallLog.getException().substring(0, 512));
            }
            if(interfaceCallLog.getContentType() != null && interfaceCallLog.getContentType().length() > 32){
                interfaceCallLog.setContentType(interfaceCallLog.getContentType().substring(0, 32));
            }
            testerExecutorMapper.insertTesterInterfaceCallLog(interfaceCallLog);

            TaskCallLog taskCallLog = new TaskCallLog();
            taskCallLog.setUpdateTime(interfaceCallLog.getCreateTime());
            taskCallLog.setTaskCallLogId(interfaceCallLog.getTaskCallLogId());
            taskCallLog.setProgres(progres);
            testerExecutorMapper.updateTesterTaskCallLogProgres(taskCallLog);
        } catch (Exception e){
            e.printStackTrace();
        }
    }

    private AnalysisResult analysisCodeBlock(String dependParam, TaskInterfaceEffectiveInfo interfaceInfo, JSONArray innate){
        AnalysisResult ret = null;
        Map<String, Object> vat = new HashMap<>();
        List<List<String>> sentenceList = WordsGenerator.generate(dependParam);
        for(List<String> sentence : sentenceList){
            if(KeyWords.RETURN.equals(sentence.get(0))){
                ret = new AnalysisResult(sentence.size() - 1);
                for(int i=1; i<sentence.size(); i++){
                    //是表达式
                    if(sentence.get(i).contains("(")){
                        Object context = analysisVariable(sentence.get(i), interfaceInfo, innate, vat);
                        ret.add(null, context);
                    } else {
                        Literal literal = new Literal(sentence.get(i));
                        if(literal.getObj() != null){
                            //是字面量
                            ret.add(null, literal.getObj());
                        } else {
                            //是变量
                            ret.add(sentence.get(i), vat.get(sentence.get(i)));
                        }
                    }
                }
                return ret;
            } else if(!KeyWords.ASSIGNMENT_OPERATOR.equals(sentence.get(1))){
                throw new RuntimeException("表达式"+sentence+"解析失败");
            }
            Object context = analysisVariable(sentence.get(2), interfaceInfo, innate, vat);
            vat.put(sentence.get(0), context);
        }
        return ret;
    }

    private Object analysisVariable(String variable, TaskInterfaceEffectiveInfo interfaceInfo, JSONArray innate, Map<String, Object> vat){
        String[] expArray = variable.split("\\.");
        Object context = null;
        for (int i = 0; i < expArray.length; i++) {
            String exp = expArray[i];
            if(exp.contains("(")){
                context = handleExpression(interfaceInfo, innate, context, exp, vat);
            } else {
                //不是函数
                Literal literal = new Literal(exp);
                if(literal.getObj() != null){
                    //是字面量
                    context = literal.getObj();
                } else {
                    //是变量
                    context = vat.get(exp);
                }
            }
        }
        return context;
    }


    //处理内置函数
    private Object handleExpression(TaskInterfaceEffectiveInfo interfaceInfo, JSONArray innate, Object body, String expression, Map<String, Object> vat){
        String exp = null;
        List<String> expParam = new LinkedList<>();
        StringBuilder sb = new StringBuilder();
        for(int i=0; i< expression.length(); i++){
            char c = expression.charAt(i);
            if(c == '('){
                exp = sb.toString().trim();
                sb.setLength(0);
            } else if(c == ',' || c == ')'){
                if(sb.length() > 0) {
                    String vn = sb.toString().trim();
                    Literal literal = new Literal(vn);
                    if(literal.getObj() == null){
                        Object vv = vat.get(vn);
                        expParam.add(vv.toString());
                    } else {
                        expParam.add(literal.getObj().toString());
                    }
                    sb.setLength(0);
                }
            } else {
                sb.append(c);
            }
        }
        ExpressionAnalysis expressionAnalysis = ExpressionContainer.getExpressionAnalysis(exp);
        ExpressionContext context = new ExpressionContext(interfaceInfo, innate, body);
        Object analysis = expressionAnalysis.analysis(context, expParam.toArray(new String[expParam.size()]));
        context.setContext(analysis);
        return analysis;
    }
}
